package net.bingosoft.common.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;


/**
 * Author: crazy_rain 
 * Date: 2007-2-14 
 * Time: 上午09:45:17 
 * Introduction:处理xml 文件的工具类
 */
public class XMLUtil {

	/** ************解析xml 元素时需要的变量 **************** */

	private static final Log log = LogFactory.getLog(XMLUtil.class);
	private static SAXReader saxReader = new SAXReader();
	private XMLUtil(){}

	/**
	 * 将xml元素输出到控制台
	 * 
	 * @param xml
	 */
	public static void dump(Node xml,String encoding) {
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding(encoding);
		XMLWriter writer;
		try {
			writer = new XMLWriter(System.out, format);
			writer.write(xml);
			writer.flush();
			// do not call writer.close(); !
		} catch (Throwable t) {
			// otherwise, just dump it
			System.out.println(xml.asXML());
		}
	}

	/**
	 * 将document 对象写入指定的文件
	 * 
	 * @param xml
	 * @param fileName
	 */
	public static void dumpToFile(Node xml, String fileName) {
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("GBK");
		try {
			XMLWriter writer = new XMLWriter(new FileWriter(fileName), format);
			writer.write(xml);
			writer.close();
		} catch (IOException e) {
			log.error("将document 对象写入指定的文件时出现IO错误 !", e);
		}
	}

	/**
	 * 获得Xml 文档对象
	 * 
	 * @param xmlFile
	 *            指向xml 文件的引用
	 * @return xmlDoc 从文件读取xml Document
	 */
	public static Document read(File xmlFile) {
		Document document = null;
		try {
			document = saxReader.read(xmlFile);
		} catch (DocumentException e) {
			log.error("通过指向xml文件的文件获得Document对象时出错 !", e);
		}
		return document;
	}

	/**
	 * 通过xml 文件的名字读取Document对象
	 * 
	 * @param xmlFileName
	 * @return Document
	 */
	public static Document read(String xmlFileName) {
		return read(new File(xmlFileName));
	}

	/**
	 * 通过指向xml 文件的URL获得Document对象
	 * 
	 * @param url
	 * @return Document
	 */
	public static Document read(URL url) {
		Document document = null;
		try {
			document = saxReader.read(url);
		} catch (DocumentException e) {
			log.error("通过指向xml文件的URL获得Document对象时出错...", e);
		}
		return document;
	}

	/**
	 * 将xml Node 对象转换成 String
	 * 
	 * @param node
	 * @return string
	 */
	public static String NodeToString(Node node) {
		return node.asXML();
	}

	/**
	 * 将字符串解析成Node
	 * 
	 * @param xmlString
	 * @return node
	 */
	public static Node StringToNode(String xmlString) {
		Node node = null;
		try {
			node = DocumentHelper.parseText(xmlString);
		} catch (DocumentException e) {
			log.error("将字符串解析成doc时出错", e);
		}
		return node;
	}

	/**
	 * 根据给定路径查询给定 xml 元素的子元素
	 * 
	 * @param parent
	 *            父节点元素
	 * @param childPath
	 *            相对与父节点的子节点路径,路径组成部分之间用"/"分割,支持通配符号"*"
	 * @return child 子节点元素
	 */
	public static Element child(Element parent, String childPath) {
		String names[] = childPath.split("/");
		Element child = parent;
		for (String name : names) {
			if (name.equals("*")) {
				child = (Element) child.elements().get(0);
			} else {
				child = child.element(name);
			}
			if (child == null) {
				log.debug("未找到指定元素[" + name + "],返回null...");
			}
		}
		return child;
	}
	/**
	 * 根据给定路径查询给定 xml 元素的子元素
	 * 
	 * @param parent
	 *            父节点元素
	 * @param childPath
	 *            相对与父节点的子节点路径,路径组成部分之间用"/"分割,支持通配符号"*"
	 * @param index 子节点在兄弟列表中的位置
	 * @return child 子节点元素
	 */
	public static Element child(Element parent, String childPath,int index) {
		String names[] = childPath.split("/");
		Element child = parent;
		for (String name : names) {
			if (name.equals("*")) {
				child = (Element) child.elements().get(index);
			} else {
				child = child.element(name);
			}
			if (child == null) {
				log.debug("未找到指定元素[" + name + "],返回null...");
			}
		}
		return child;
	}

	/**
	 * 查询父节点的子节点的属性值
	 * 
	 * @param parent
	 *            父节点
	 * @param attributePath
	 *            子节点属性相对于父节点的路径,路径各组成部分用"/"分割, 属性名称前要添加"@"符号
	 *            支持通配符号"*"
	 * @return 子节点的属性值,如果找不到返回null
	 */
	public static String childAttribute(Element parent, String attributePath) {
		if (attributePath.indexOf('@') == -1)
			throw new IllegalArgumentException("属性查询要使用 '@'");
		int slashLoc = attributePath.lastIndexOf('/');
		String childPath = attributePath.substring(0, slashLoc);
		Element child = child(parent, childPath);
		String attributeName = attributePath.substring(slashLoc + 2);
		String attributeValue = child.attributeValue(attributeName);
		if (null == attributeValue) {
			log.debug("未找到指定属性[" + attributeName + "],返回null...");
		}
		return attributeValue;
	}

	/**
	 * 根据相对于父节点的子节点路径,查询子节点列表
	 * 
	 * @param parent
	 *            父节点
	 * @param childrenPath
	 *            子节点路径
	 * @return children 子节点列表
	 */
	public static List children(Element parent, String childrenPath) {
		int slashLoc = childrenPath.lastIndexOf('/');
		Element child = child(parent, childrenPath.substring(0, slashLoc));
		if (child == null)
			return Collections.EMPTY_LIST;
		String childrenName = childrenPath.substring(slashLoc + 1);
		List children;
		if (childrenName.equals("*")) {
			children = child.elements();
		} else {
			children = child.elements(childrenName);
		}
		return children;
	}
	/**
	 * 创建一个xml 元素
	 * @param name xml 元素的名称
	 * @param attributes 元素属性
	 * @param ns 命名空间
	 * @return
	 */
	public static Element createElement(String name,Map<String,String> attributes,Namespace ns){
		Element element = null;
		if(ns == null){
			element = DocumentHelper.createElement(name);
		}else{
			element = DocumentHelper.createElement(new QName(name,ns));
		}
		for(String key: attributes.keySet()){
			element.addAttribute(key, attributes.get(key));
		}
		return element;
	}
	/**
	 * 创建一个xml 元素
	 * @param name xml 元素的名称
	 * @param attributes 元素属性
	 * @return
	 */
	public static Element createElement(String name,Map<String,String> attributes){
		return createElement(name,attributes,null);
	}
	/**
	 * 创建一个xml 元素
	 * @param name xml 元素的名称
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Element createElement(String name){
		return createElement(name,Collections.EMPTY_MAP);
	}
	/**
	 * 创建xml 文档
	 * @param nsArray 命名空间数组
	 * @param root
	 * @return
	 */
	public static Document createDocument(Namespace[]nsArray,Element root){
		Document document = DocumentHelper.createDocument();
		if(root != null){
			document.add(root);	
		}
		if(nsArray != null && nsArray.length > 0){
			for(Namespace ns : nsArray){
				document.add(ns);
			}
		}
		return document;
	}
	/**
	 * 创建xml 文档
	 * @param nsArray 命名空间数组
	 * @param root
	 * @return
	 */
	public static Document createDocument(Namespace[]nsArray,String rootName){
		Document document = DocumentHelper.createDocument();
		
		if(nsArray != null && nsArray.length > 0){
			for(Namespace ns : nsArray){
				document.add(ns);
			}
		}
		if(rootName != null){
			document.add(createElement(rootName));	
		}
		return document;
	}
	/**
	 * 创建xml 文档
	 * @param root 根元素
	 * @return
	 */
	public static Document createDocument(Element root){
		return createDocument(null, root);
	}
}


